import axios, { AxiosInstance } from 'axios';
import { ElMessage, ElMessageBox } from 'element-plus';
import { Session } from '@/utils/storage';
import { user,systemConfig } from '@/store';
import { encryptSM4,decryptSM2 } from '@/utils/util';
import { signatureGenerate,getFileSignParams, signFileParams } from '@/utils/signatureUtil';
import router from '@/router';
import qs from 'qs';
const VITE_API_URL = import.meta.env.VITE_API_URL as string;
const LOCAL_TOKEN_NAME = import.meta.env.VITE_LOCAL_TOKEN_NAME as string;
const LOCAL_REFRESHTOKEN_NAME = import.meta.env.VITE_LOCAL_REFRESHTOKEN_NAME as string;
const LOGIN_PATH = import.meta.env.VITE_LOGIN_PATH as string;
const ACCESS_TOKEN = import.meta.env.VITE_ACCESS_TOKEN as string;
const VITE_REFRESH_TOKEN = import.meta.env.VITE_REFRESH_TOKEN as string;
const API_TIMEOUT = import.meta.env.VITE_API_TIMEOUT as string;
const VITE_APP_ID = import.meta.env.VITE_APP_ID as string;
const VITE_REPEAT_ENANLED= import.meta.env.VITE_REPEAT_ENANLED as string;
const COMMON_URL = '/doc/api/';
// 存储由于 token 过期导致 失败的请求
let expiredRequestArr: any[] = [];
let expiredReqArr: any[] = [];
// 避免频繁发送更新 
let firstRequre = true;
// 避免频繁发送更新 
let firstReq= true;
// 正在进行中的请求列表
let reqList = [] // 如果某个api存在这个数组里，说明该api暂时无法再次请求
/**
 * 阻止重复请求
 * @param {array} reqList - 请求缓存列表
 * @param {string} url - 当前请求地址
 * @param {function} cancel - 请求中断函数
 * @param {string} errorMessage - 请求中断时需要显示的错误信息
 */
const stopRepeatRequest = function (reqList, url, cancel, errorMessage) {
	const errorMsg = errorMessage || ''
	for (let i = 0; i < reqList.length; i++) {
		if (reqList[i] === url) {
			cancel(errorMsg)
			return
		}
	}
	reqList.push(url)
  }
/**
 * 允许某个请求可以继续进行
 * @param {array} reqList 全部请求列表
 * @param {string} url 请求地址
 */
const allowRequest = function (reqList, url) {
	if(url){
		for (let i = 0; i < reqList.length; i++) {
			if (reqList[i] === url) {
				reqList.splice(i, 1)
				break
			}
		}
	}else{
		reqList=[]
	}
	
  }
// 配置新建一个 axios 实例
const service: AxiosInstance = axios.create({
	baseURL: '',
	timeout: Number(API_TIMEOUT) || 10000,
	headers: { 'Content-Type': 'application/json' },
	paramsSerializer: {
		serialize(params) {
			return qs.stringify(params, { allowDots: true });
		},
	},
});

// 添加请求拦截器
service.interceptors.request.use(
	(config) => {
		const repeatStr=config.headers.repeatEnabled
		let repeatEnabled=false;
		if(repeatStr==='true'){
			repeatEnabled=true;
		}
		// 获取请求头参数
		if(config.headers.fileServiceFlag){
			const {signature, timestamp} = signFileParams();
			(<any>config.headers)['signature'] = signature;
			(<any>config.headers)['timestamp'] = timestamp;
		}else{
			const {signature, timestamp,dataSign} = signatureGenerate(config);
			if(JSON.parse(VITE_REPEAT_ENANLED)&&repeatEnabled){
				let cancel		
				// 设置cancelToken对象
				config.cancelToken = new axios.CancelToken(function(c) {
					cancel = c
				})
				
				// 阻止重复请求。当上个请求未完成时，相同的请求不会进行
				stopRepeatRequest(reqList, dataSign, cancel, `${config.url} 请求被中断`);
			}
			
			
			(<any>config.headers)['signature'] = signature;
			(<any>config.headers)['timestamp'] = timestamp;
			(<any>config.headers)['x-bifang-session'] = dataSign;
			(<any>config.headers)['x-encrty-split'] = systemConfig.splitFlag;
		}
		
		// 在发送请求之前做些什么 token
		const token = Session.get(LOCAL_TOKEN_NAME);
		if (token) {
			(<any>config.headers)[ACCESS_TOKEN] = token;
		}
		const currentRouter: any = user.currentRouter;
		let routerId = '';
		if (currentRouter) {
			if (currentRouter.meta) {
				routerId = currentRouter.meta.routerId;
			}
		}
		(<any>config.headers)['routerId'] = routerId;
		(<any>config.headers)['appId'] = VITE_APP_ID;
		return config;
	},
	(error) => {
		if(JSON.parse(VITE_REPEAT_ENANLED)){
			// 增加延迟，相同请求不得在短时间内重复发送
			let dataSign="";
			if(error.headers){
				dataSign=error.headers['x-bifang-session']
			}
			
			setTimeout(() => {
				allowRequest(reqList, dataSign)
			}, 100) // 上一次请求返回后过1s才允许再次请求
		}
		// 对请求错误做些什么
		return Promise.reject(error);
	}
);

// 添加响应拦截器
service.interceptors.response.use(
	(response) => {
		// 对响应数据做点什么
		let res = response?.data;
		if (!res) {
			res = response;
		}
		const encrtyStr = response.headers['x-encrty-enabled']
		
		const code = res.code;
		if (code && code !== 0 && code !== 200) {
			if (code === 401) {
				ElMessageBox.confirm('当前用户已登出，请尝试重新登录后再操作。', '提示', {
					type: 'error',
					closeOnClickModal: false,
					center: true,
					confirmButtonText: '重新登录',
				})
					.then(async () => {
						Session.remove(LOCAL_TOKEN_NAME);
						Session.remove(LOCAL_REFRESHTOKEN_NAME);
						const currentRouter: any = user.currentRouter;
						let query = { redirect: currentRouter.path, params: JSON.stringify(currentRouter.query) };
						router.replace({ path: LOGIN_PATH, query: query });
						
					})
					.catch(() => {});
					return res;
			} else if (code === 403) {
				ElMessage.error(res.message || '没有操作权限！');
				return res;
			}else if (code === 456) {
				Session.remove(LOCAL_TOKEN_NAME);
				if(!Session.get(LOCAL_REFRESHTOKEN_NAME)){
					ElMessageBox.confirm('当前用户已登出，请尝试重新登录后再操作。', '提示', {
						type: 'error',
						closeOnClickModal: false,
						center: true,
						confirmButtonText: '重新登录',
					})
						.then(async () => {
							Session.remove(LOCAL_TOKEN_NAME);
							Session.remove(LOCAL_REFRESHTOKEN_NAME);
							const currentRouter: any = user.currentRouter;
							let query = { redirect: currentRouter.path, params: JSON.stringify(currentRouter.query) };
							router.replace({ path: LOGIN_PATH, query: query });
							
						})
						.catch(() => {});
						return res;
				}else{
					/**
					 * refreshToken 为封装的有关更新 token 的相关操作 
					 */
					
					let data=response.config.oldData;
					let params=response.config.oldParams;
					if(data){
						response.config.data=data
					}
					if(params){
						response.config.params=params
					}
					if (firstRequre) {
						
						let res=updateTokenByRefreshToken(response.config)
						return  res;
					}else{
						//如果当前已经是处于刷新token的状态，就将请求置于请求队列中，这个队列会在刷新token的回调中执行，由于new关键子存在声明提升，所以不用顾虑会有请求没有处理完的情况，这段添加请求的程序一定会在刷新token的回调执行之前执行的
						return new Promise(resolve => {
							//这里加入的是一个promise的解析函数，将响应的config配置对应解析的请求函数存到requests中，等到刷新token回调后再执行
							expiredRequestArr.push(() => {
								resolve(service(response.config));
							})
						})
					}
				}
				
			}else if (code === 457) {
				Session.remove("setAppCryptoFlag");
				if(firstReq){
					let res=setAppSecretKey(response.config)
					return  res;
				}else{
					return new Promise(resolve => {
						//这里加入的是一个promise的解析函数，将响应的config配置对应解析的请求函数存到requests中，等到刷新token回调后再执行
						expiredReqArr.push(() => {
							resolve(service(response.config));
						})
					})
				}
				
			}
			if (code < 0) {
				let errorMsg = res.message || '系统异常(后端未返回异常原因)';
				ElMessage.error(errorMsg);
			}
			return Promise.reject(res);
		} else {
			if(JSON.parse(VITE_REPEAT_ENANLED)){
				// 增加延迟，相同请求不得在短时间内重复发送
				let dataSign='';
				if(response.headers){
					dataSign=response.headers['x-bifang-session']
				}
				
				setTimeout(() => {
					allowRequest(reqList, dataSign)
				}, 100) // 上一次请求返回后过1s才允许再次请求
			}
			if(encrtyStr==='true'){
				//解密报文
				let result=res.result;
				if(result){
					let secretKey='';
					
					let decrytStr=decryptSM2(result,secretKey)
					try{
						res.result=JSON.parse(decrytStr)
					}catch(e){
						res.result=decrytStr
					}
					
					return res;
				}
				return res;
			}
			return res;
		}
	},
	(error) => {
		if(JSON.parse(VITE_REPEAT_ENANLED)){
			if (axios.isCancel(error)) {
				console.log(error.message);
			} else {
				let dataSign="";
				if(error.response?.headers){
					dataSign=error.response?.headers['x-bifang-session'];
				}
				// 增加延迟，相同请求不得在短时间内重复发送
				setTimeout(() => {
					allowRequest(reqList, dataSign)
				}, 100) // 请求失败返回后过1s才允许再次请求
			}	
		}	
		// 对响应错误做点什么
		if (error.message.indexOf('timeout') != -1) {
			ElMessage.error('网络超时');
		} else if (error.message == 'Network Error') {
			ElMessage.error('网络连接错误');
		} else {
			if (error.response) {
				if (error.response.status == 404) {
					ElMessage.error('Status:404，正在请求不存在的服务器记录！');
				} else if (error.response.status == 403) {
					ElMessage.error('没有操作权限！');
				}else if (error.response.status == 500) {
					ElMessage.error(error.response.data.message || 'Status:500，服务器发生错误！');
				} else if (error.response.status == 401) {
					ElMessageBox.confirm('当前用户已被登出，请尝试重新登录后再操作。', '提示', {
						type: 'error',
						closeOnClickModal: false,
						center: true,
						confirmButtonText: '重新登录',
					})
					.then(() => {
						Session.remove(LOCAL_TOKEN_NAME);
						const currentRouter: any = user.currentRouter;
						let query = { redirect: currentRouter.path, params: JSON.stringify(currentRouter.query) };
						router.replace({ path: LOGIN_PATH, query: query });
						
					})
					.catch(() => {});
					return;
				} else {
					if (error.response.data) ElMessage.error(error.response.statusText);
					else ElMessage.error('接口路径找不到');
				}
			} else if (error.code) {
				if(error.code===461){
					ElMessage.error(error.message);
				}
				//后端返回的错误提示已在上面处理，这里遇到直接返回
				return;
			} else {
				ElMessage.error('请求服务器无响应！');
			}
		}

		return Promise.reject(error);
	}
);
/**
 * 利用 refreshToken 更新当前使用的 token
 */
const updateTokenByRefreshToken = (reqConfig:any) => {
	return new Promise(resolve => {
		firstRequre = false;
		let config: any = {};
		let headers = { 'func-code': 'base0018',cryptoEnabled:'true' };
		config.headers = headers;
		let params={} as any;
		params[VITE_REFRESH_TOKEN]=Session.get(LOCAL_REFRESHTOKEN_NAME);
		let url=VITE_API_URL + COMMON_URL + 'base0018'
		http.post(url, params, config).then(res => {
			const result = res.result;
			Session.set(LOCAL_TOKEN_NAME, result.accessToken);
			Session.set(LOCAL_REFRESHTOKEN_NAME, result.refreshToken);
			Session.set('USERSECRET',encryptSM4(JSON.stringify(result.secretKey),systemConfig.defaultSM4Key));
			user.setUserSecret(result.secretKey);
			/**
		 * 当获取了最新的 refreshToken, accessToken 后
		 * 重新发起之前失败的请求
		 */
			expiredRequestArr.forEach(run => run())
			expiredRequestArr = [];
			resolve(service(reqConfig));
		}).catch(()=>{
			console.log('刷新 token 失败err');
			
		}).finally(()=>{
			firstRequre = true;
		});
	})
  }
  const setAppSecretKey = (reqConfig:any) => {
	return new Promise(resolve => {
		firstReq = false;
		let config: any = {};
		let headers = { 'func-code': 'base0017' };
		config.headers = headers;
		let params={} as any;
		params.appId=encryptSM4(VITE_APP_ID,systemConfig.defaultSM4Key);
		let url=VITE_API_URL + COMMON_URL + 'base0017'
		http.post(url, params, config).then(() => {
			Session.set("setAppCryptoFlag","true");
			expiredReqArr.forEach(run => run())
			expiredReqArr = [];
			resolve(service(reqConfig));
		}).catch(()=>{
			console.log('刷新 token 失败err');
			
		}).finally(()=>{
			firstReq = true;
		});
	})
  }
const http = {
	/** get 请求
	 * @param  {接口地址} url
	 * @param  {请求参数} params
	 * @param  {参数} config
	 */
	get: function (url: any, params?: any, config?: any): any {
		
		return service({
			method: 'get',
			url: url,
			params: params,
			...config,
		});
	},

	/** post 请求
	 * @param  {接口地址} url
	 * @param  {请求参数} data
	 * @param  {参数} config
	 */
	post: function (url: any, data?: any, config?: any): any {
		
		return service({
			method: 'post',
			url: url,
			data: data,
			...config,
		});
	},

	/** put 请求
	 * @param  {接口地址} url
	 * @param  {请求参数} data
	 * @param  {参数} config
	 */
	put: function (url: any, data?: object | null | undefined, config?: object): any {
		return service({
			method: 'put',
			url: url,
			data: data,
			...config,
		});
	},

	/** patch 请求
	 * @param  {接口地址} url
	 * @param  {请求参数} data
	 * @param  {参数} config
	 */
	patch: function (url: any, data?: object | null | undefined, config?: object): any {
		return service({
			method: 'patch',
			url: url,
			data: data,
			...config,
		});
	},

	/** delete 请求
	 * @param  {接口地址} url
	 * @param  {请求参数} data
	 * @param  {参数} config
	 */
	delete: function (url: any, data?: object | null | undefined, config?: object): Promise<any> {
		return service({
			method: 'delete',
			url: url,
			data: data,
			...config,
		});
	},

	/** jsonp 请求
	 * @param  {接口地址} url
	 * @param  {JSONP回调函数名称} name
	 */
	jsonp: function (url: any, name = 'jsonp'): Promise<any> {
		return new Promise((resolve) => {
			var script = document.createElement('script');
			var _id = `jsonp${Math.ceil(Math.random() * 1000000)}`;
			script.id = _id;
			script.type = 'text/javascript';
			script.src = url;
			(<any>window)[name] = (response: any) => {
				resolve(response);
				document.getElementsByTagName('head')[0].removeChild(script);
				try {
					delete (<any>window)[name];
				} catch (e) {
					(<any>window)[name] = undefined;
				}
			};
			document.getElementsByTagName('head')[0].appendChild(script);
		});
	},
	downFile: function (url, parameter, method, timeout) {
		if (!method) {
			method = 'post';
		}
		if (method === 'get') {
			return service({
				url: url,
				params: parameter,
				method: method,
				timeout: timeout || 120000,
				headers: {
					'Content-Type': 'application/json; application/octet-stream',
					fileServiceFlag:true
				},
				responseType: 'blob',
			});
		} else {
			return service({
				url: url,
				data: parameter,
				method: method,
				timeout: timeout || 120000,
				headers: {
					'Content-Type': 'application/json; application/octet-stream',
					fileServiceFlag:true
				},
				responseType: 'blob',
			});
		}
	},
	/**
	 * 下载文件
	 * @param url 文件路径
	 * @param fileName 文件名
	 * @param parameter
	 * @param method 请求方法，默认为post
	 * @returns {*}
	 */
	downloadFile: function (url, fileName, parameter, method, timeout) {
		return this.downFile(url, parameter, method, timeout).then(async (data: any) => {
			if (data.type === 'application/json') {
				const text = await data.text();
				const jsonText = await JSON.parse(text);
				console.error(jsonText.message);
				return;
			}
			if (typeof window.navigator?.msSaveBlob !== 'undefined') {
				window.navigator.msSaveBlob(new Blob([data]), fileName);
			} else {
				let url = window.URL.createObjectURL(new Blob([data]));
				var a = document.createElement('a');
				document.body.appendChild(a);
				a.href = url;
				if (fileName) {
					a.download = fileName;
				}
				a.click();
				window.URL.revokeObjectURL(url);
				document.body.removeChild(a); //下载完成移除
			}
		});
	},
	/**
	 * 文件上传 用于富文本上传图片
	 * @param url
	 * @param parameter
	 * @returns {*}
	 */
	uploadFile: function (url, parameter) {
		
		return service({
			url: url,
			data: parameter,
			method: 'post',
			timeout: 60000,
			headers: {
				'Content-Type': 'multipart/form-data', // 文件上传
				fileServiceFlag:true
			},
		});
	},
};
// 导出 axios 实例
export default http;
